package com.gitee.wsl.struct.either.ext

import arrow.core.Either
import arrow.core.recover
import kotlinx.coroutines.flow.Flow
import kotlin.contracts.InvocationKind
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract

suspend fun <L, R> Either<L, R>.handle(
    onLeftAction: suspend (L) -> Unit = {},
    onRightAction: suspend (R) -> Unit = {},
) = when (this) {
    is Either.Left -> onLeftAction(this.value)
    is Either.Right -> onRightAction(this.value)
}

suspend fun <L, R, T> Either<L, R>.handleAndGet(
    onLeftAction: suspend (L) -> T,
    onRightAction: suspend (R) -> T,
) = when (this) {
    is Either.Left -> onLeftAction(this.value)
    is Either.Right -> onRightAction(this.value)
}

suspend fun <L, R> Either<L, R>.rightOrError(message: String) = handleAndGet(
    onLeftAction = { error(message) },
    onRightAction = { it },
)


suspend fun <L, R> Flow<Either<L, R>>.collectAndHandle(
    onLeftAction: suspend (L) -> Unit = {},
    onRightAction: suspend (R) -> Unit = {},
) = collect { either ->
    either.handle(onLeftAction, onRightAction)
}

/**
 * Recovers the left side of an Either with the supplied function
 */

@OptIn(ExperimentalContracts::class)
inline fun <A, B, C> Either<A, B>.handleErrorWith(f: (A) -> Either<C, B>): Either<C, B> {
    contract { callsInPlace(f, InvocationKind.AT_MOST_ONCE) }
    return recover { a -> f(a).bind() }
}